﻿/*
 * Author: Gabriel Reiser
 * Contributors:
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Irrlicht.Net.Core
{
    [Serializable]
    public struct Vector3D
    {
        public float x;
        public float y;
        public float z;

        public float X { get { return x; } set { x = value; } }
        public float Y { get { return y; } set { y = value; } }
        public float Z { get { return z; } set { z = value; } }
        public float Length { get { return (float)Math.Sqrt(x * x + y * y + z * z); } }
        public float LengthSquared { get { return x * x + y * y + z * z; } }
        public static readonly Vector3D Zero = new Vector3D(0.0f, 0.0f, 0.0f);
        public static readonly Vector3D UnitX = new Vector3D(1.0f, 0.0f, 0.0f);
        public static readonly Vector3D UnitY = new Vector3D(0.0f, 1.0f, 0.0f);
        public static readonly Vector3D UnitZ = new Vector3D(0.0f, 0.0f, 1.0f);
        public static readonly Vector3D Unit = new Vector3D(1.0f, 1.0f, 1.0f);

        /*public Vector3D()
        {
            this.x = 0f;
            this.y = 0f;
            this.z = 0f;
        }*/

        public Vector3D(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public static Vector3D operator +(Vector3D vec1, Vector3D vec2)
        {
            return new Vector3D(vec1.x + vec2.x, vec1.y + vec2.y, vec1.z + vec2.z);
        }
        public static Vector3D operator -(Vector3D vec1, Vector3D vec2)
        {
            return new Vector3D(vec1.x - vec2.x, vec1.y - vec2.y, vec1.z - vec2.z);
        }
        public static Vector3D operator -(Vector3D vec1, float value)
        {
            return new Vector3D(vec1.x - value, vec1.y - value, vec1.z - value);
        }
        public static Vector3D operator -(Vector3D vector)
        {
            return new Vector3D(-vector.x, -vector.y, -vector.z);
        }
        public static Vector3D operator *(Vector3D vec1, Vector3D vec2)
        {
            return new Vector3D(vec1.x * vec2.x, vec1.y * vec2.y, vec1.z * vec2.z);
        }
        public static Vector3D operator *(Vector3D vec1, float value)
        {
            return new Vector3D(vec1.x * value, vec1.y * value, vec1.z * value);
        }
        public static Vector3D operator /(Vector3D vec1, Vector3D vec2)
        {
            return new Vector3D(vec1.x / vec2.x, vec1.y / vec2.y, vec1.z / vec2.z);
        }
        public static Vector3D operator /(Vector3D vec1, float value)
        {
            float inverse = 1.0f / value;
            return vec1 * inverse;
        }

        public static bool operator == (Vector3D vec1, Vector3D vec2)
        {
            if ((vec1.x == vec2.x) && (vec1.y == vec2.y) && (vec1.z == vec2.z))
                return true;
            else
                return false;
        }
        public static bool operator !=(Vector3D vec1, Vector3D vec2)
        {
            if (vec1.x != vec2.x || vec1.y != vec2.y || vec1.z != vec2.z)
                return true;
            else
                return false;
        }
        public static bool operator >(Vector3D vec1, Vector3D vec2)
        {
            if ((vec1.x > vec2.x) && (vec1.y > vec2.y) && (vec1.z > vec2.z))
                return true;
            else
                return false;
        }
        public static bool operator <(Vector3D vec1, Vector3D vec2)
        {
            if ((vec1.x < vec2.x) && (vec1.y < vec2.y) && (vec1.z < vec2.z))
                return true;
            else
                return false;
        }
        public static Vector3D Multiply(Vector3D vec1, Vector3D vec2)
        {
            return vec1 * vec2;
        }
        public void Inverse()
        {
            this.x *= -1;
            this.y *= -1;
            this.z *= -1;
        }
        public static Vector3D Inverse(Vector3D vector)
        {
            return -vector;
        }
        public static float Dot(Vector3D vec1, Vector3D vec2)
        {
            double dot = 0.0;
            dot += vec1.x * vec2.x;
            dot += vec1.y * vec2.y;
            dot += vec1.z * vec2.z;
            return (float)dot;
        }

        public float Dot(Vector3D vector)
        {
            return Dot(this, vector);
        }

        public static Vector3D Cross(Vector3D vec1, Vector3D vec2)
        {
            Vector3D c = new Vector3D();
            c.x = (vec1.y * vec2.z) - (vec1.z * vec2.y);
            c.y = (vec1.z * vec2.x) - (vec1.x * vec2.z);
            c.z = (vec1.x * vec2.y) - (vec1.y * vec2.x);
            return c;
        }

        public Vector3D Cross(Vector3D vector)
        {
            return Cross(this, vector);
        }

        public void Normalize()
        {
            // Will also work for zero-sized vectors, but will change nothing
            if (Length > float.Epsilon)
            {
                float inverseLength = 1.0f / Length;

                this.x *= inverseLength;
                this.y *= inverseLength;
                this.z *= inverseLength;
            }
        }

        public override bool Equals(object obj)
        {
            if (obj is Vector3D)
                return (this == (Vector3D)obj);
            else
                return false;
        }

        public override int GetHashCode()
        {
            return (int)(x + y + z);
        }

    }
}
